package net.worldscale.xp.wslib.urm.loader;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;

import net.worldscale.xp.wslib.urm.BadUriException;
import net.worldscale.xp.wslib.urm.URM;
import net.worldscale.xp.wslib.urm.io.IConnection;
import net.worldscale.xp.wslib.urm.io.IHttpConnection;
import net.worldscale.xp.wslib.urm.util.ICommandConnection;
import net.worldscale.xp.wspr.IThreadEntry;
import net.worldscale.xp.wspr.WSPR;

public class ImplDefaultLoader extends Loader {

    private final String mOriginURI;
    private ILoaderListener mListener = null;
    private boolean mIsStarted = false;
    private InputStream mIS = null;
    private OutputStream mOS = null;
    private IConnection mConn = null;
    private int mPhase = Loader.PHASE_INIT;
    private int mRespCode = 0;
    private String mMimeType = "";
    private long mLength = 0;
    private long mCountBytesLoaded = 0;
    private byte[] mData = null;
    private String mNextURI;

    public ImplDefaultLoader(String uri) {
        mOriginURI = uri;
    }

    @Override
    public void cancel() {
        throw new RuntimeException("// TODO Auto-generated method stub");
    }

    @Override
    public void setListener(ILoaderListener l) {
        this.mListener = l;
    }

    @Override
    public void loadAsync() {
        if (!mIsStarted) {
            mIsStarted = true;
            IThreadEntry te = new IThreadEntry() {

                @Override
                public void run() {
                    loadProc();
                }
            };
            WSPR.getInstance().getThreadManager().newThread(te).start();
        }
    }

    @Override
    public void loadSync() {
        if (!mIsStarted) {
            mIsStarted = true;
            loadProc();
        }
    }

    private boolean loadProcConn(IHttpConnection conn) throws IOException,
            URMLoaderException {

        final int respCode = conn.getResponseCode();
        this.mRespCode = respCode;
        switch (respCode) {
            case IHttpConnection.HTTP_OK:
                break;
            default:
                return true;
        }
        final String mime = conn.getType();
        this.mMimeType = mime;
        final ILoaderListener l = this.mListener;
        if (l == null) {
            int iii = 0;
            iii++;
        } else {
            if (!l.onMimeType(this, mime)) {
                this.mPhase = Loader.PHASE_NO_ACCEPT_MIME;
                System.out.println("Unsupported MIME Type:");
                System.out.println("     URI = " + this.mOriginURI);
                System.out.println("    Type = " + mime);
                return true;
            }
        }
        final long length = conn.getLength();
        this.mLength = length;
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        final InputStream is = conn.openInputStream();
        this.mIS = is;
        int cntBytes = 0;
        for (int b = is.read(); 0 <= b; b = is.read()) {
            baos.write(b);
            cntBytes++;
            this.mCountBytesLoaded = cntBytes;
        }
        final byte[] ba = baos.toByteArray();
        this.mData = ba;
        this.mLength = ba.length;
        return true;
    }

    private URMLoaderException newURMLoaderException(int phase) {
        this.mPhase = phase;
        final String msg;
        switch (phase) {
            case Loader.PHASE_RETRY_TIMEOUT:
                msg = "retry timeout";
                break;
            case Loader.PHASE_BAD_SCHEME:
                msg = "bad scheme";
                break;
            case Loader.PHASE_NO_LISTENER:
                msg = "no loader listener";
                break;
            default:
                msg = "unknow";
        }
        return new URMLoaderException(msg);
    }

    private boolean loadProcRetry() throws IOException, URMLoaderException,
            BadUriException {
        final String uri = this.mNextURI;
        final IConnection conn = URM.getInstance().getConnector().open(uri);
        if (conn == null) {
            throw newURMLoaderException(Loader.PHASE_BAD_SCHEME);
        }
        mConn = conn;
        if (conn instanceof IHttpConnection) {
            return this.loadProcConn((IHttpConnection) conn);
        } else if (conn instanceof ICommandConnection) {
            return this.loadProcConn((ICommandConnection) conn);
        }
        throw newURMLoaderException(Loader.PHASE_BAD_SCHEME);
    }

    private boolean loadProcConn(ICommandConnection conn) {
        conn.execute();
        return true;
    }

    private void loadProc() {
        try {
            this.mNextURI = this.mOriginURI;
            for (int retry = 10; retry > 0; retry--) {
                if (loadProcRetry()) {
                    this.closeAll();
                    final ILoaderListener l = this.mListener;
                    if (l != null) {
                        l.onFinish(this);
                    }
                    return;
                } else {
                    this.closeAll();
                }
            }
            this.mPhase = PHASE_RETRY_TIMEOUT;
            throw new URMLoaderException("retry timeout , uri="
                    + this.mOriginURI);
        } catch (Exception e) {
            final PrintStream out = System.out;
            e.printStackTrace(out);
            out.println("    URI = " + this.mOriginURI);
        }
        this.closeAll();
    }

    private void closeAll() {

        try {
            final InputStream is = mIS;
            mIS = null;
            if (is != null) {
                is.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            final OutputStream os = mOS;
            mOS = null;
            if (os != null) {
                os.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            final IConnection conn = mConn;
            mConn = null;
            if (conn != null) {
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public long getLoadedLength() {
        return this.mCountBytesLoaded;
    }

    @Override
    public String getMimeType() {
        return this.mMimeType;
    }

    @Override
    public long getTotalLength() {
        return this.mLength;
    }

    @Override
    public InputStream openInputStream() {
        final byte[] ba = this.mData;
        if (ba == null) {
            return null;
        } else {
            return new ByteArrayInputStream(ba);
        }
    }

    @Override
    public String getFinalURI() {
        return this.mNextURI;
    }

    @Override
    public String getOriginURI() {
        return this.mOriginURI;
    }

    @Override
    public String getURI() {
        return this.mOriginURI;
    }

    @Override
    public int getPhase() {
        return this.mPhase;
    }

    @Override
    public int getResponseCode() {
        return this.mRespCode;
    }
}
